package net.towee.client.com.repo.presenter;

import java.util.Arrays;
import java.util.List;

import net.towee.client.ToweeGinjector;
import net.towee.client.com.app.AppController.Component;
import net.towee.client.com.repo.DiscussionShower;
import net.towee.client.com.repo.RepositoryDocumentEditor;
import net.towee.client.com.repo.RepositoryDocumentShower;
import net.towee.client.com.repo.RepositoryNodeSelector;
import net.towee.client.event.core.DocumentReceivedEvent;
import net.towee.client.event.core.DocumentReceivedHandler;
import net.towee.client.history.HistoryManager;
import net.towee.client.managers.AccountHelper;
import net.towee.client.managers.AttachmentsManager;
import net.towee.client.managers.DocumentsManager;
import net.towee.client.managers.NodesManager;
import net.towee.model.account.AccountPermission;
import net.towee.model.discussion.DiscussionSubject;
import net.towee.model.repo.AttachmentIdentity;
import net.towee.model.repo.AttachmentInfo;
import net.towee.model.repo.DocumentData;
import net.towee.model.repo.DocumentNode;
import net.towee.model.repo.NodeData;

import com.google.common.base.Preconditions;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.logical.shared.HasOpenHandlers;
import com.google.gwt.event.logical.shared.HasSelectionHandlers;
import com.google.gwt.event.logical.shared.OpenEvent;
import com.google.gwt.event.logical.shared.OpenHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.inject.Inject;

public class RepositoryDocumentPresenter implements RepositoryDocumentShower,
		SelectionHandler<Integer>, ClickHandler, OpenHandler<AttachmentIdentity> {

	public interface Display extends IsWidget {
		/**
		 * Show document. It must clear old showed document and show newes
		 * 
		 * @param doc
		 */
		void showDocument(DocumentData doc, boolean canEdit);

		/**
		 * By this object we get needed revision and show it by showDocument
		 * method
		 * 
		 * @return
		 */
		HasSelectionHandlers<Integer> getRevisionSelector();
		
		/**
		 * @return attachment openner
		 */
		HasOpenHandlers<AttachmentIdentity> getAttachmentOpenner();
		
		/**
		 * Show extended information about attachment
		 * @param data
		 */
		void showAttachmentData(AttachmentInfo data);
		
		/**
		 * Select given revision. If revision number <0 means, that we must
		 * select last revision.
		 * 
		 * By default last revision selected.
		 * 
		 * @param revision
		 */
		void selectRevision(int revision);

		/**
		 * Button for edit document
		 * 
		 * @return
		 */
		HasClickHandlers getDocumentEditorButton();

		/**
		 * @return container for discussion
		 */
		HasWidgets getDiscussinoContainer();
		
		/**
		 * @return container for path
		 */
		HasWidgets getPathSelectorContainer();
		
		/*
		 * Events
		 */
		void onDocumentLoading();

		void onDocumentLoaded();
	}

	private final DocumentsManager docsManager;
	private final Display display;
	private final HandlerManager eventBus;
	private final NodesManager nodesManager;
	private final ToweeGinjector injector;
	private final AttachmentsManager attManager;
	private final RepositoryNodeSelector selector;
	private final HistoryManager history;
	private final DiscussionShower discussion;
	private final AccountHelper account;
	
	private DocumentNode docNode;
	private DocumentData currentDoc;
	private HasWidgets container;

	@Inject
	public RepositoryDocumentPresenter(Display display, DocumentsManager docsManager, HandlerManager eventBus,
			NodesManager nodesManager, ToweeGinjector injector, AttachmentsManager attManager,
			RepositoryNodeSelector selector, HistoryManager history, DiscussionShower discussion,
			AccountHelper account) {
		this.display = display;
		this.docsManager = docsManager;
		this.eventBus = eventBus;
		this.nodesManager = nodesManager;
		this.injector = injector;
		this.attManager = attManager;
		this.selector = selector;
		this.history = history;
		this.discussion = discussion;
		this.account = account;

		// Bind events
		bindEvents();
	}
	
	/**
	 * Return true if user can edit document in given group
	 * @param groupId
	 * @return
	 */
	private boolean canEdit(String groupId, String authorId){
		return account.getAccount().getAccountIdentity().getId().equals(authorId) ||
				account.hasPermissions(groupId, AccountPermission.CAN_CHANGE_OTHER_DOCUMENT);
	}

	private void bindEvents() {
		display.getRevisionSelector().addSelectionHandler(this);
		display.getDocumentEditorButton().addClickHandler(this);
		display.getAttachmentOpenner().addOpenHandler(this);
		
		selector.addSelectionHandler(new SelectionHandler<NodeData>() {
			@Override
			public void onSelection(SelectionEvent<NodeData> event) {
				injector.getRepositoryShower().showTreeNode( event.getSelectedItem() );
			}
		});
		
		eventBus.addHandler(DocumentReceivedEvent.TYPE, new DocumentReceivedHandler() {
			@Override
			public void onDocumentReceived(DocumentData result, boolean local) {
				if(currentDoc != null && currentDoc.getIdentity().equals( result.getIdentity() )) {
					// Set current doc
					currentDoc = result;
					
					// Show document and select last revision
					display.showDocument(currentDoc, canEdit(result.getNode().getGroupId(), result.getIdentity().getAuthor().getId()));
					display.selectRevision(-1);
				}
			}
		});
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<NodeData> getSelectedNodes() {
		return (List<NodeData>) (List<?>) Arrays.asList(docNode);
	}

	@Override
	public NodeData getShowedNode() {
		return docNode;
	}

	@Override
	public void setNode(NodeData node) {
		Preconditions.checkNotNull(node.toDocumentNode(), "node not a document");
		
		this.docNode = node.toDocumentNode();
	}

	@Override
	public void go(HasWidgets cont) {
		Preconditions.checkNotNull(docNode, "null document node");
		
		// Set container
		this.container = cont;

		// Show progress bar
		display.onDocumentLoading();

		// Get document
		docsManager.getDocument(docNode.getDocumentIdentity().getId(), new AsyncCallback<DocumentData>(){
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(DocumentData result) {
				// Hide progress bar
				display.onDocumentLoaded();

				// Show it
				container.clear();
				container.add( display.asWidget() );
				
				// Show document
				currentDoc = result;
				display.showDocument(result, canEdit(result.getNode().getGroupId(), result.getIdentity().getAuthor().getId()));
				
				// Show discussion
				discussion.setContainerId( result.getIdentity().getId() );
				discussion.setDiscussion( result.getDiscussion() );
				discussion.setSubject( DiscussionSubject.DOCUMENT );
				discussion.go( display.getDiscussinoContainer() );
			}
		});
		
		// Show node selector
		nodesManager.getNode(docNode.getParentId(), docNode.getGroupId(), new AsyncCallback<NodeData>(){
			@Override
			public void onFailure(Throwable caught) {	
			}

			@Override
			public void onSuccess(NodeData result) {
				selector.setCurrentNode( result );
				selector.go( display.getPathSelectorContainer() );
			}
		});
	}

	/**
	 * On select some revision
	 */
	@Override
	public void onSelection(SelectionEvent<Integer> event) {
		final int rev = event.getSelectedItem(); 
		
		// Show progress bar
		display.onDocumentLoading();

		// Get revision
		docsManager.getDocumentRevision(currentDoc.getIdentity().getId(), rev,  new AsyncCallback<DocumentData>(){
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(DocumentData result) {
				// Hide progress bar
				display.onDocumentLoaded();

				// Show document
				currentDoc = result;
				display.showDocument(result, canEdit(result.getNode().getGroupId(), result.getIdentity().getAuthor().getId()));
				display.selectRevision(rev);
			}
		});
	}

	@Override
	public void onClick(ClickEvent event) {
		// Get parent node
		nodesManager.getNode( docNode.getParentId(), docNode.getGroupId(), new AsyncCallback<NodeData>(){
			@Override
			public void onFailure(Throwable caught) {	
			}

			@Override
			public void onSuccess(NodeData result) {
				// Create editor
				RepositoryDocumentEditor editor = injector.getRepositoryDocumentEditor();
				editor.setDocument(currentDoc, result);
				
				// Show editor
				editor.go(container);
				
				// Add history event
				history.addHistoryItem(Component.REPO, "group", docNode.getGroupId(), "node", docNode.getId(), "edit", "");
			}
		});
	}
	
	@Override
	public void onOpen(OpenEvent<AttachmentIdentity> event) {
		attManager.getAttachmentInfo(event.getTarget().getId(), new AsyncCallback<AttachmentInfo>(){
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(AttachmentInfo result) {
				display.showAttachmentData(result);
			}
		});
	}
	
	@Override
	public void removeNodes(List<NodeData> cutNodes) {
	}

	@Override
	public void showNodes(List<NodeData> cutNodes) {
	}
}
